package jmine.selenium.services;

import static jmine.selenium.services.SeleniumApplicationService.DEFAULT_SESSION;

import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.Locale;

import jmine.tec.di.annotation.Injected;
import jmine.tec.persist.PersistMessages;
import jmine.tec.services.annotations.Input;
import jmine.tec.services.api.ServiceExecutionException;
import jmine.tec.services.api.ServiceProperties;
import jmine.tec.test.testcase.TestCase;
import bancosys.tec.persist.Persistable;
import bancosys.tec.persist.dao.BaseDAO;
import bancosys.tec.persist.dao.BaseDAOFactory;
import bancosys.tec.persist.dao.BeanNotFoundException;
import bancosys.tec.utils.date.Date;
import bancosys.tec.utils.date.DateUtils;

import com.thoughtworks.selenium.Selenium;

/**
 * Classe base de serviços de teste de selenium escritos em java.
 * 
 * @author Rafael Volpato
 */
public abstract class AbstractSeleniumService extends TestCase {

    public static final String SELENIUM_SESSION = "Session";

    // **************************************************************************************
    // Metodos utilizados para executar serviços de selenium utilizando a infra de Serviços
    // **************************************************************************************

    private static final int CEM = 100;

    private ServiceProperties serviceProperties;

    private BaseDAOFactory daoFactory;

    private Selenium selenium;

    protected static final int NUMBER_DIGIT_8 = 8;

    /**
     * @param serviceProperties the serviceProperties to set
     */
    @Injected
    public void setServiceProperties(ServiceProperties serviceProperties) {
        this.serviceProperties = serviceProperties;
    }

    /**
     * @param daoFactory the daoFactory to set
     */
    @Injected
    public void setDaoFactory(BaseDAOFactory daoFactory) {
        this.daoFactory = daoFactory;
    }

    /**
     * @param selenium the selenium to set
     */
    @Input(fieldName = SELENIUM_SESSION, defaultValue = DEFAULT_SESSION)
    public void setSelenium(Selenium selenium) {
        this.selenium = selenium;
    }

    /**
     * @return the serviceProperties
     */
    protected final ServiceProperties getServiceProperties() {
        return this.serviceProperties;
    }

    /**
     * @return daoFactory
     */
    public BaseDAOFactory getDaoFactory() {
        return this.daoFactory;
    }

    /**
     * @return the selenium
     */
    protected final Selenium getSelenium() {
        return this.selenium;
    }

    // **************************************************************************************
    // Metodos auxiliares para o tratamento dos service properties
    // **************************************************************************************

    /**
     * Atalho para pegar propriedade.
     * 
     * @param <T> classe que vai ser retornada
     * @param key propriedade
     * @param clazz classe que deve ser retornada
     * @return valor da propriedade
     */
    protected final <T> T getProperty(String key, Class<T> clazz) {
        return this.getServiceProperties().get(key, clazz);
    }

    /**
     * Atalho para obter o valor de uma propriedade que é uma String (forma mais utilizada).
     * 
     * @param key propriedade
     * @return valor da propriedade
     */
    protected final String getProperty(String key) {
        return this.getProperty(key, String.class);
    }

    /**
     * Atalho para obter o valor de uma propriedade que é uma String (forma mais utilizada).
     * 
     * @param key propriedade
     * @param defaultValue se não for encontrado nenhum valor associado a propriedade
     * @return valor da propriedade
     */
    protected final String getProperty(String key, String defaultValue) {
        String value = this.getProperty(key);
        if (value != null) {
            return value;
        } else {
            return defaultValue;
        }
    }

    /**
     * Atalho para obter o valor de uma propriedade que é uma String (forma mais utilizada).
     * 
     * @param key propriedade
     * @param defaultValue se não for encontrado nenhum valor associado a propriedade
     * @return valor da propriedade
     */
    protected final String getRawProperty(String key, String defaultValue) {
        String value = this.getRaw(key);
        if (value != null) {
            return value;
        } else {
            return defaultValue;
        }
    }

    /**
     * Atalho para obter uma propriedade na forma de um objeto date.
     * 
     * @param key property
     * @return Data formatada dd/mm/aaaaa ou <code>null</code>
     */
    protected final String getDateProperty(String key) {
        Date date = DateUtils.getCalendarByViewDateDDMMYYYY(this.getProperty(key));
        if (date != null) {
            return this.format(date);
        } else {
            return null;
        }
    }

    /**
     * Atalho para obter uma propriedade na forma de um objeto Integer.
     * 
     * @param key property
     * @return Integer
     */
    protected final String getNumberProperty(String key) {
        String propertyValue = this.getProperty(key);
        if (propertyValue == null) {
            return null;
        }

        NumberFormat numberFormat = NumberFormat.getNumberInstance(new Locale("pt", "br"));
        return numberFormat.format(Double.parseDouble(propertyValue));
    }

    /**
     * Atalho para obter uma propriedade na forma de um objeto Integer.
     * 
     * @param key property
     * @param defaultValue se não for encontrado nenhum valor associado a propriedade
     * @return Integer
     */
    protected final String getNumberProperty(String key, String defaultValue) {
        String propertyValue = this.getNumberProperty(key);
        if (propertyValue == null) {
            propertyValue = defaultValue;
        }
        return propertyValue;
    }

    /**
     * Função que formata a propriedade na forma númerica.
     * 
     * @param key Property
     * @param numeroMaximoCasasDecimais numeroMaximoCasasDecimais Número máximo de casas decimais
     * @return String Propriedade formatada
     */
    protected final String getNumberProperty(String key, int numeroMaximoCasasDecimais) {
        String propertyValue = this.getProperty(key);
        if (propertyValue == null) {
            return null;
        }

        NumberFormat numberFormat = NumberFormat.getNumberInstance(new Locale("pt", "br"));
        numberFormat.setMaximumFractionDigits(numeroMaximoCasasDecimais);
        return numberFormat.format(Double.parseDouble(propertyValue));
    }

    /**
     * Função que formata a propriedade na forma númerica.
     * 
     * @param key Property
     * @param defaultValue se não for encontrado nenhum valor associado a propriedade
     * @param numeroMaximoCasasDecimais numeroMaximoCasasDecimais Número máximo de casas decimais
     * @return String Propriedade formatada
     */
    protected final String getNumberProperty(String key, String defaultValue, int numeroMaximoCasasDecimais) {
        String propertyValue = this.getNumberProperty(key, numeroMaximoCasasDecimais);
        if (propertyValue == null) {
            propertyValue = defaultValue;
        }
        return propertyValue;
    }

    /**
     * Atalho para obter uma propriedade na forma de um objeto Integer.
     * 
     * @param key property
     * @return Integer
     */
    protected final String getPercentProperty(String key) {
        String propertyValue = this.getProperty(key);
        if (propertyValue == null) {
            return null;
        }

        NumberFormat numberFormat = NumberFormat.getNumberInstance(new Locale("pt", "br"));
        return numberFormat.format(Double.parseDouble(propertyValue) * CEM);
    }

    /**
     * Atalho para obter uma propriedade na forma de um objeto boolean.
     * 
     * @param key property
     * @return boolean
     */
    protected final boolean getBoolean(String key) {
        String property = this.getProperty(key, "false");
        return property.toUpperCase().equals("SIM") || property.toUpperCase().equals("TRUE");
    }

    /**
     * Retorna o valor associado a chave informada, no seu formato original (sem uppercase).
     * 
     * @param key key
     * @return o valor associado a chave informada
     */
    protected final String getRaw(String key) {
        return this.getServiceProperties().getRaw(key);
    }

    /**
     * Retorna o valor associado a chave informada, no seu formato original (sem uppercase).
     * 
     * @param key key
     * @param defaultValue se não for encontrado nenhum valor associado a propriedade
     * @return o valor associado a chave informada
     */
    protected final String getRaw(String key, String defaultValue) {
        String value = this.getServiceProperties().getRaw(key);
        if (value != null) {
            return value;
        } else {
            return defaultValue;
        }
    }

    /**
     * Converte uma data para {@link String}.
     * 
     * @param date data
     * @return {@link String}
     * @see DateUtils#dateToStringDDMMYYYY(java.util.Date)
     */
    protected final String format(Date date) {
        return DateUtils.dateToStringDDMMYYYY(date);
    }

    /**
     * Converte um numero para {@link String}.
     * 
     * @param number numero
     * @return {@link String}
     */
    protected final String format(BigDecimal number) {
        NumberFormat numberFormat = NumberFormat.getNumberInstance(new Locale("pt", "br"));
        numberFormat.setMaximumFractionDigits(NUMBER_DIGIT_8);
        return numberFormat.format(number);
    }

    /**
     * Salva a referencia a um objeto do sistema no {@link ServiceProperties}.
     * 
     * @param key key
     * @param identification identification
     * @param referenceClass referenceClass
     * @throws ServiceExecutionException se ocorrer algum erro
     * @param <T> Tipo do persistable que vai ser salvo
     */
    protected <T extends Persistable> void saveReference(String key, String identification, Class<T> referenceClass)
            throws ServiceExecutionException {
        if (key == null) {
            return;
        }

        String variableName = key.startsWith("$") ? key : "$" + key;
        BaseDAO<T> dao = this.daoFactory.getGenericDAO(referenceClass);

        try {
            T reference = dao.findByNaturalKey(identification);
            this.getServiceProperties().putDefinition(variableName, reference);
        } catch (BeanNotFoundException e) {
            throw new ServiceExecutionException(PersistMessages.BEAN_NOT_FOUND_BY_NATURAL_KEY.create(referenceClass, identification), e);
        }
    }

    // **************************************************************************************
    // Metodos auxiliares para comunicação com o selenium
    // **************************************************************************************

    /**
     * Retorna o texto que vai ser usado como identificador de um objeto.
     * 
     * @param locator elemento da tela que possui o texto
     * @return o texto que vai ser usado como identificador de um objeto
     */
    protected final String getIdentification(String locator) {
        String result = this.getSelenium().getText(locator);
        return result.replaceAll("\\.", "");
    }
}
